home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / GFSOCKET.PY < prev    next >
Encoding:
Python Source  |  1999-07-28  |  7.1 KB  |  225 lines

  1.  
  2.  
  3. """socket interactions for gadfly client and server"""
  4.  
  5. from select import select
  6.  
  7. # responses
  8.  
  9. SUCCESS = "SUCCESS"
  10. EXCEPTION = "EXCEPTION"
  11.             
  12. def reply_exception(exception, info, socket):
  13.     """send an exception back to the client"""
  14.     # any error is invisible to client
  15.     from gfserve import ServerError
  16.     import sys
  17.     try:
  18.         reply( (EXCEPTION, (exception, info)), socket)
  19.     except:
  20.         #info = "%s %s" % (sys.exc_type, sys.exc_value)
  21.         socket.close()
  22.         #raise ServerError, "reply_exception failed: "+`info`
  23.         
  24. def reply_success(data, socket):
  25.     """report success with data back to client"""
  26.     reply( (SUCCESS, data), socket)
  27.         
  28. def reply(data, socket):
  29.     from marshal import dumps
  30.     marshaldata = dumps(data)
  31.     send_packet(socket, marshaldata)
  32.     socket.close()
  33.     
  34. def send_packet(socket, data):
  35.     """blast out a length marked packet"""
  36.     send_len(data, socket)
  37.     socket.send(data)
  38.     
  39. def send_len(data, socket):
  40.     """send length of data as cr terminated int rep"""
  41.     info = `len(data)`+"\n"
  42.     socket.send(info)
  43.     
  44. def send_certified_action(actor_name, action, arguments, password, socket):
  45.     from marshal import dumps
  46.     marshaldata = dumps( (action, arguments) )
  47.     cert = certificate(marshaldata, password)
  48.     #print actor_name, cert,  marshaldata
  49.     marshaldata = dumps( (actor_name, cert, marshaldata) )
  50.     send_packet(socket, marshaldata)
  51.     
  52. def unpack_certified_data(data):
  53.     from marshal import loads
  54.     # sanity check
  55.     unpack = (actor_name, certificate, marshaldata) = loads(data)
  56.     return unpack
  57.     
  58. def recv_data(socket, timeout=10):
  59.     """receive data or time out"""
  60.     from time import time
  61.     endtime = time() + timeout
  62.     reader = Packet_Reader(socket)
  63.     done = 0
  64.     while not done:
  65.         timeout = endtime - time()
  66.         if timeout<0:
  67.             raise IOError, "socket time out (1)"
  68.         (readable, dummy, error) = select([socket], [], [socket], timeout)
  69.         if error:
  70.             raise IOError, "socket in error state"
  71.         if not readable:
  72.             raise IOError, "socket time out (2)"
  73.         reader.poll()
  74.         done = (reader.mode==READY)
  75.     return reader.data
  76.     
  77. def interpret_response(data):
  78.     """interpret response data, raise exception if needed"""
  79.     from marshal import loads
  80.     (indicator, data) = loads(data)
  81.     if indicator==SUCCESS:
  82.         return data
  83.     elif indicator==EXCEPTION:
  84.         # ???
  85.         raise EXCEPTION, data
  86.     else:
  87.         raise ValueError, "unknown indicator: "+`indicator`
  88.     
  89. # packet reader modes
  90. LEN = "LEN"
  91. DATA = "DATA"
  92. READY = "READY"
  93. ERROR = "ERROR"
  94.  
  95. BLOCK_SIZE = 4028
  96.  
  97. LEN_LIMIT = BLOCK_SIZE * 10
  98.     
  99. class Packet_Reader:
  100.     """nonblocking pseudo-packet reader."""
  101.     
  102.     # packets come in as decimal_len\ndata
  103.     # (note: cr! not crlf)
  104.     
  105.     # kick too large requests if set
  106.     limit_len = LEN_LIMIT
  107.     
  108.     def __init__(self, socket):
  109.         self.socket = socket
  110.         self.length = None
  111.         self.length_remaining = None
  112.         self.len_list = []
  113.         self.data_list = []
  114.         self.received = ""
  115.         self.data = None
  116.         self.mode = LEN
  117.         
  118.     def __len__(self):
  119.         if self.mode is LEN:
  120.             raise ValueError, "still reading length"
  121.         return self.length
  122.         
  123.     def get_data(self):
  124.         if self.mode is not READY:
  125.             raise ValueError, "still reading"
  126.         return self.data
  127.         
  128.     def poll(self):
  129.         mode = self.mode
  130.         if mode is READY:
  131.             raise ValueError, "data is ready"
  132.         if mode is ERROR:
  133.             raise ValueError, "socket error previously detected"
  134.         socket = self.socket
  135.         (readable, dummy, error) = select([socket], [], [socket], 0)
  136.         if error:
  137.             self.socket.close()
  138.             self.mode = ERROR
  139.             raise ValueError, "socket is in error state"
  140.         if readable:
  141.             if mode is LEN:
  142.                 self.read_len()
  143.             # note: do not fall thru automatically
  144.             elif mode is DATA:
  145.                 self.read_data()
  146.                 
  147.     def read_len(self):
  148.         """assume socket is readable now, read length"""
  149.         socket = self.socket
  150.         received = self.received
  151.         len_list = self.len_list
  152.         if not received:
  153.             # 10 bytes at a time until len is read.
  154.             received = socket.recv(10)
  155.         while received:
  156.             # consume, test one char
  157.             input = received[0]
  158.             received = received[1:]
  159.             if input == "\n":
  160.                 # done reading length
  161.                 from string import join, atoi
  162.                 try:
  163.                     length = self.length = atoi(join(len_list, ""))
  164.                 except:
  165.                     self.mode = ERROR
  166.                     socket.close()
  167.                     raise ValueError, "bad len string? "+`len_list`
  168.                 self.received = received
  169.                 self.length_remaining = length
  170.                 self.mode = DATA
  171.                 limit_len = self.limit_len
  172.                 if limit_len and length>limit_len:
  173.                     raise ValueError, "Length too big: "+`(length, limit_len)`
  174.                 return
  175.             if len(len_list)>10:
  176.                 self.mode = ERROR
  177.                 socket.close()
  178.                 raise ValueError, "len_list too long: "+`len_list`
  179.             len_list.append(input)
  180.             if not received:
  181.                 (readable, dummy, error) = select(\
  182.                    [socket], [], [socket], 0)
  183.                 if error:
  184.                     self.mode = ERROR
  185.                     socket.close()
  186.                     raise ValueError, "socket in error state"
  187.                 if readable:
  188.                     received = socket.recv(10)
  189.         # remember extra data received.
  190.         self.received = received
  191.  
  192.     def read_data(self):
  193.         # assume socket is readable
  194.         socket = self.socket
  195.         received = self.received
  196.         length_remaining = self.length_remaining
  197.         data_list = self.data_list
  198.         if received:
  199.             data_list.append(received)
  200.             self.received = ""
  201.             length_remaining = length_remaining - len(received)
  202.         recv_len = max(length_remaining, BLOCK_SIZE)
  203.         received = socket.recv(recv_len)
  204.         if received:
  205.             data_list.append(received)
  206.             length_remaining = length_remaining - len(received)
  207.         if length_remaining<1:
  208.             self.mode = READY
  209.             from string import join
  210.             self.data = join(data_list, "")
  211.         self.length_remaining = length_remaining
  212.  
  213. def certificate(String, password):
  214.     """generate a certificate for a string, using a password"""
  215.     from md5 import new
  216.     if not String:
  217.         raise ValueError, "cannot generate certificate for empty string"
  218.     taggedstring = password + String
  219.     return new(taggedstring).digest()
  220.     
  221. def certify(String, cert, password):
  222.     """check a certificate for a string"""
  223.     return certificate(String, password) == cert
  224.  
  225.